﻿using System.ComponentModel;
using System.Security.Claims;
using System.Text;
using Application.Common.Interfaces;
using Application.Features.Auth.DTOs;
using Application.Features.Users.DTOs;
using AutoMapper;
using Common.Attributes;
using Common.Caching;
using Common.Configurations;
using Common.Extensions;
using Common.Interfaces;
using Common.Models;
using Common.Utils;
using Domain.Repositories;
using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;

namespace Application.Features.Auth.Commands;

#nullable disable

/// <summary>
/// 刷新令牌
/// </summary>
[Description("刷新令牌")]
[GroupDescription("授权管理")]
public class RefreshTokenCommand : IRequest<RefreshTokenOutput>
{
    /// <summary>
    /// 刷新令牌
    /// </summary>
    public string RefreshToken { get; set; }
}
/// <summary>
/// 处理程序
/// </summary>
public class RefreshTokenCommandHandler : IRequestHandler<RefreshTokenCommand, RefreshTokenOutput>
{
    private readonly ICurrentUserService _currentUserService;
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly ICacheService _cacheService;
    private readonly ITokenService _tokenService;
    private readonly IOptions<JwtSettings> _optJwtSettings;
    private readonly IMenuRepository _menuRepository;
    private readonly IUserRepository _repository;
    private readonly IMapper _mapper;
    private readonly IApplicationDbContext _dbContext;

    public RefreshTokenCommandHandler(
        ICurrentUserService currentUserService,
        IHttpContextAccessor httpContextAccessor,
        ICacheService cacheService,
        ITokenService tokenService,
        IOptions<JwtSettings> optJwtSettings,
        IMenuRepository menuRepository,
        IUserRepository repository,
        IMapper mapper,
        IApplicationDbContext dbContext)
    {
        _currentUserService = currentUserService;
        _httpContextAccessor = httpContextAccessor;
        _cacheService = cacheService;
        _tokenService = tokenService;
        _optJwtSettings = optJwtSettings;
        _menuRepository = menuRepository;
        _repository = repository;
        _mapper = mapper;
        _dbContext = dbContext;
    }



    /// <summary>
    /// 业务逻辑
    /// </summary>
    /// <param name="request">请求参数</param>
    /// <param name="cancellationToken">取消标记</param>
    /// <returns>返回处理结果</returns>
    public async Task<RefreshTokenOutput> Handle(RefreshTokenCommand request, CancellationToken cancellationToken)
    {

        #region 验签
        var jwtSecurityToken = _tokenService.Decode(request.RefreshToken);
        var claims = jwtSecurityToken?.Claims?.ToArray();
        if (!claims!.Any())
            throw new InvalidOperationException("登录过期，请重新登录！");

        var refreshExpires = claims!.FirstOrDefault(a => a.Type == ApplicationClaimTypes.RefreshExpires)?.Value;
        if (refreshExpires!.IsNullOrEmpty() || refreshExpires!.ToInt64OrDefault() <= DateTime.Now.ToUnixTimestampSeconds())
            throw new InvalidOperationException("登录过期，请重新登录！");

        var securityKey = _optJwtSettings.Value.SecurityKey;
        var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(securityKey)), SecurityAlgorithms.HmacSha256);
        var input = jwtSecurityToken!.RawHeader + "." + jwtSecurityToken.RawPayload;
        if (jwtSecurityToken.RawSignature != JwtTokenUtilities.CreateEncodedSignature(input, signingCredentials))
            throw new InvalidOperationException("登录过期，请重新登录！");
        #endregion

        #region 刷新令牌
        var userId = claims![0].Value.ToInt64OrDefault();

        var user = await _repository.FindByIdAsync(userId);
        if (user == null) throw new InvalidOperationException($"登录过期，请重新登录！");

        _cacheService.Remove(user.Id.ToString().ToAuthUserCaCheKey());
        _cacheService.Remove(user.Id.ToString().ToAuthUserPathsCaCheKey());

        if (user.IsActive == false) throw new InvalidOperationException($"账号已被停用！");

        //var userRoles = await _dbContext.Roles
        //   .Include(r => r.UserRoles)
        //   .Where(x => x.UserRoles.Any(ur => ur.UserId == user.Id))
        //   .ToListAsync();

        var userMenus = await _menuRepository.GetUserMenusByUserIdAsync(user.Id, user.IsSystemAccount ?? false);
        var allowAccessPaths = userMenus.Where(x => x.Meta.Hidden == false).Select(menu => (menu.HttpMethods + menu.Url)).ToList();
        List<Claim> newClaims = new()
        {
            new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
            new Claim(ClaimTypes.Name, user.UserName!),
            new Claim(ApplicationClaimTypes.IsSystemAccount, user.IsSystemAccount?.ToString()??"False"),
            new Claim(ApplicationClaimTypes.ProfilePictureDataUrl, user.ProfilePictureDataUrl ?? ""),
            new Claim(ApplicationClaimTypes.TenantName, user.TenantName ?? ""),
            new Claim(ApplicationClaimTypes.TenantId, user.TenantId ?? "")
        };

        string accessToken = _tokenService.Build(newClaims, _optJwtSettings.Value);
        string refreshToken = _tokenService.Build(newClaims, _optJwtSettings.Value, true);

        var data = new RefreshTokenOutput
        {
            Username = user.UserName!,
            UserInfo = _mapper.Map<UserOutput>(user),
            //Roles = userRoles?.Select(s => s.RoleCode).ToList(),
            AccessToken = accessToken,
            RefreshToken = refreshToken,
            Expires = DateTime.Now.AddSeconds(_optJwtSettings.Value.ExpireSeconds).ToString("yyyy/MM/dd HH:mm:ss"),
        };

        _cacheService.Set(
            user.Id.ToString().ToAuthUserCaCheKey(),
            data.ToJson(),
            TimeSpan.FromSeconds(_optJwtSettings.Value.ExpireSeconds));

        _cacheService.Set(
            user.Id.ToString().ToAuthUserPathsCaCheKey(),
           allowAccessPaths,
            TimeSpan.FromSeconds(_optJwtSettings.Value.ExpireSeconds));
        #endregion

        return data;
    }
}